//-----------------------------------------------------------------------------
// F330_AtmlDemo2.c
//-----------------------------------------------------------------------------
//
// AUTH: IN
// DATE: JUL 2009
//
// This program controls the Demo UUT for ATML Demo Phase II.
//
// Target: C8051F330/1/2/3/4/5
//
// Tool chain: KEIL Eval 'c'
//

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <c8051f330.h>                    // SFR declarations
#include <string.h>

//-----------------------------------------------------------------------------
// Pin Declarations
//-----------------------------------------------------------------------------

sbit POT_CLK   =  P0^0;                 // 1->0 transtion to command potentiometer change (TBD - verify)
sbit POT_U_nD  =  P0^1;                 // POT_U/nD ='1' means UP
sbit LED       =  P1^3;                 // LED ='1' means ON

//-----------------------------------------------------------------------------
// 16-bit SFR Definitions for 'F33x
//-----------------------------------------------------------------------------

sfr16 TMR2RL   = 0xca;                    // Timer2 reload value
sfr16 TMR2     = 0xcc;                    // Timer2 counter

//-----------------------------------------------------------------------------
// Global CONSTANTS
//-----------------------------------------------------------------------------

#define SYSCLK       24500000             // SYSCLK frequency in Hz
#define BAUDRATE     9600                 // Baud rate of UART in bps

//-----------------------------------------------------------------------------
// Function PROTOTYPES
//-----------------------------------------------------------------------------
void SYSCLK_Init (void);
void PORT_Init (void);
void Timer2_Init (int counts);
void Timer2_ISR (void);
void UART0_Init (void);

//-----------------------------------------------------------------------------
// Global Variables
//-----------------------------------------------------------------------------

#define UART_BUFFERSIZE 64
unsigned char UART_Buffer[UART_BUFFERSIZE];
unsigned char UART_Buffer_Size = 0;
unsigned char UART_Input_First = 0;
static char UART_Byte;
static char GPIO_Byte;
static char GPIO_ByteTmp;
unsigned char potPulseInProgress = 0;

unsigned char currentPotSetting;     		// current potentiometer setting
unsigned char reqPotSetting; 				// required potentiometer setting
unsigned char reqOutputIsOn; 				// required output state (1 means ON)
 
//-----------------------------------------------------------------------------
// MAIN Routine
//-----------------------------------------------------------------------------
void main (void) {

   // disable watchdog timer
   PCA0MD &= ~0x40;                       // WDTE = 0 (clear watchdog timer enable)

   SYSCLK_Init ();                        // Initialize system clock to 24.5MHz
   
   PORT_Init ();                          // Initialize crossbar and GPIO
   
   Timer2_Init (SYSCLK / 12 / 10);        // Init Timer2 to generate interrupts at a 10Hz rate

   UART0_Init();						  // Initialize UART

   EA = 1;							      // enable global interrupts

   currentPotSetting = 0x40;			  // current potentiometer setting; matches potentiometer setting after power-up 
   reqOutputIsOn = 1;                     // when starting, turn output ON
   
   while (1)                              // loop continuously
   {
	   // process level specified via GPIO
		GPIO_ByteTmp = P1 & 0xb7; 				// get bits from P1; mask unused lines
		GPIO_ByteTmp |= (P0 & 0x04)<<1;			// add D3 bit
		GPIO_ByteTmp |= (P0 & 0x08)<<3;			// add D6 bit
		GPIO_Byte = (GPIO_ByteTmp>>1) & 0x7f;	// convert to 7 bits integer (potentiometer has 128 notches)

		// determine required setting
		if(reqOutputIsOn == 1)
			reqPotSetting = GPIO_Byte;
		else
			reqPotSetting = 0x00;
	}
}

//-----------------------------------------------------------------------------
// SYSCLK_Init
//-----------------------------------------------------------------------------
//
// This routine initializes the system clock to use the internal 24.5MHz
// oscillator as its clock source.  Also enables missing clock detector reset.
//
void SYSCLK_Init (void)
{
   OSCICN |= 0x03;                     // Configure internal oscillator for its maximum frequency

   RSTSRC = 0x04;                         // enable missing clock detector
}

//-----------------------------------------------------------------------------
// PORT_Init
//-----------------------------------------------------------------------------
//
// Configure the Crossbar and GPIO ports
//
//-------------------------------
//| P0 | 7  6  5  4  3  2  1  0 |
//-------------------------------
//       |  |  |  |  |  |  |  |
//       |  |  |  |  |  |  |   --- POT_CLK
//       |  |  |  |  |  |   ------ POT_U_nD
//       |  |  |  |  |   --------- D3
//       |  |  |  |   ------------ D6
//       |  |  |   --------------- TX
//       |  |   ------------------ RX
//       |   --------------------- GPIO (not used)
//        ------------------------ GPIO (not used)

//-------------------------------
//| P1 | 7  6  5  4  3  2  1  0 |
//-------------------------------
//       |  |  |  |  |  |  |  |
//       |  |  |  |  |  |  |   --- D0
//       |  |  |  |  |  |   ------ D1
//       |  |  |  |  |   --------- D2
//       |  |  |  |   ------------ LED (internal); when port line is 0, LED is ON
//       |  |  |   --------------- D4
//       |  |   ------------------ D5
//       |   --------------------- Potentiometer (internal, not used)
//        ------------------------ D7

void PORT_Init (void)
{

   XBR0     = 0x01;                       // UART I/O Output Enable
   XBR1     = 0x40;                       // Enable crossbar and weak pull-ups

   P0MDIN |= 0x0c;                     	  // D<n> lines are digital
   P1MDIN |= 0xb7;                        // D<n> lines are digital

   P0     |= 0xff;                        // Set latches to '1' for digital inputs
   P1     |= 0xff;                        // Set latches to '1' for digital inputs

   P0MDOUT |= 0x03;                       // enable POT_CLK and POT_U/nD as a push-pull outputs
   P1MDOUT |= 0x08;                       // enable LED as a push-pull output

   P0     |= 0x03;                        // Set POT_CLK and POT_U_nD to '1' 
   P1     |= 0x08;                        // Set LED to OFF

	// Note: with no digital stimulus applied, input ports will read "1"
}

//-----------------------------------------------------------------------------
// UART0_Init
//-----------------------------------------------------------------------------
//
// Configure the UART0 using Timer1, for <BAUDRATE> and 8-N-1.
//

void UART0_Init (void)
{
   SCON0 = 0x10;                       // SCON0: 8-bit variable bit rate, level of STOP bit is ignored, RX enabled
                                       //        ninth bits are zeros, clear RI0 and TI0 bits
   if (SYSCLK/BAUDRATE/2/256 < 1) {
      TH1 = -(SYSCLK/BAUDRATE/2);
      CKCON &= ~0x0B;                  // T1M = 1; SCA1:0 = xx
      CKCON |=  0x08;
   } else if (SYSCLK/BAUDRATE/2/256 < 4) {
      TH1 = -(SYSCLK/BAUDRATE/2/4);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 01
      CKCON |=  0x01;
   } else if (SYSCLK/BAUDRATE/2/256 < 12) {
      TH1 = -(SYSCLK/BAUDRATE/2/12);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 00
   } else {
      TH1 = -(SYSCLK/BAUDRATE/2/48);
      CKCON &= ~0x0B;                  // T1M = 0; SCA1:0 = 10
      CKCON |=  0x02;
   }

   TL1 = TH1;                          // init Timer1
   TMOD &= ~0xf0;                      // TMOD: timer 1 in 8-bit autoreload
   TMOD |=  0x20;
   TR1 = 1;                            // START Timer1
   IP |= 0x10;                         // Make UART high priority
   ES0 = 1;                            // Enable UART0 interrupts
}

//-----------------------------------------------------------------------------
// Timer2_Init
//-----------------------------------------------------------------------------
//
// Configure Timer2 to 16-bit auto-reload and generate an interrupt at
// interval specified by <counts> using SYSCLK/12 as its time base.
//
void Timer2_Init (int counts)
{
   TMR2CN  = 0x00;                        // stop Timer2; clear TF2; use Timer2 as 16-bit timer with SYSCLK/12 as timebase
   CKCON  &= ~0x60;                       // Timer2 clocked based on T2XCLK;

   TMR2RL  = -counts;                     // Init reload values
   TMR2    = 0xffff;                      // set to reload immediately
   ET2     = 1;                           // enable Timer2 interrupts
   TR2     = 1;                           // start Timer2
}

//-----------------------------------------------------------------------------
// Interrupt Service Routines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Timer2_ISR
//-----------------------------------------------------------------------------
//
// This routine implemens potentiometer control, synchronous with Timer2 interrupts
//
void Timer2_ISR (void) interrupt 5
{
   TF2H = 0;                              // clear Timer2 interrupt flag

	if(potPulseInProgress == 1) // a pulse was started in the previous interrupt
	{
		POT_CLK = 1;    // restore clock line
		LED = 1;		// restore LED to OFF
		potPulseInProgress = 0;
	}
	else
	{
	   // command potentiometer up or down one notch, if necessary
	   if(currentPotSetting < reqPotSetting)
	   {
	   		// count up
			LED = 0;		// pulse LED for visual feedback

			POT_U_nD = 1;   // set count direction to UP
			POT_CLK = 0;    // start pulse on clock line
			potPulseInProgress = 1;
		
			currentPotSetting++;
	   }
	   else if(currentPotSetting > reqPotSetting)
	   {
	   		// count down
			LED = 0;		// pulse LED for visual feedback

			POT_U_nD = 0;   // set count direction to DOWN
			POT_CLK = 0;    // start pulse on clock line
			potPulseInProgress = 1;
		
			currentPotSetting--;
	   }
	}
}

//-----------------------------------------------------------------------------
// UART0_Interrupt
//-----------------------------------------------------------------------------
//
// This routine is invoked whenever a character is received or transmission of a character is completed
// It implements character reception in a buffer, as a zero-terminated string
//

void UART0_Interrupt (void) interrupt 4
{
   if (RI0 == 1)
   {
      RI0 = 0;                           		// clear interrupt flag

      if( UART_Buffer_Size == 0)         		// buffer is empty
	  {
         UART_Input_First = 0;    
	     UART_Buffer[UART_Input_First] = 0x00; 	// terminate by zero
	   }

      UART_Byte = SBUF0;                      	// read a character from UART

      if (UART_Buffer_Size < UART_BUFFERSIZE - 1) // leave room for terminal zero
      {
         UART_Buffer[UART_Input_First] = UART_Byte;  	// record in buffer (this overwrites the terminal zero)
         UART_Buffer[UART_Input_First+1] = 0x00; 		// set again the terminal zero

         UART_Buffer_Size++;             		

         UART_Input_First++;             
	  }

	   // process commands received via UART
	   if(UART_Buffer_Size == 1)
	   {
		   if(strcmp(UART_Buffer, "R") != 0)	// invalid character - discard
			{
				UART_Buffer_Size = 0;         // empty buffer 
		     	UART_Input_First = 0;    
		     	UART_Buffer[UART_Input_First] = 0x00; 	
			}
	   }
	   else if(UART_Buffer_Size == 2)
	   {
		   	if(strcmp(UART_Buffer, "R0") == 0)	// TBD: check function return value
		   	{
				reqOutputIsOn = 0;
		        SBUF0 = 'k';                  // acknowledge message reception
		   	}
		   	else if(strcmp(UART_Buffer, "R1") == 0)	// TBD: check function return value
		   	{
				reqOutputIsOn = 1;
		        SBUF0 = 'k';                  // acknowledge message reception
		   	}
			UART_Buffer_Size = 0;         // empty buffer 
	     	UART_Input_First = 0;    
	     	UART_Buffer[UART_Input_First] = 0x00; 	
		}
		else if(UART_Buffer_Size > 2) // defensive - should never get here
		{
			UART_Buffer_Size = 0;         // empty buffer 
	     	UART_Input_First = 0;    
	     	UART_Buffer[UART_Input_First] = 0x00; 	
		}

   }
   if (TI0 == 1)                   		 		// "transmit complete" flag is set
   {
      TI0 = 0;                           		// clear interrupt flag
	  // nothing else to do
   }
}